package main

import (
	"fmt"
	"sync"
	"time"
)

type Map struct {
	c   map[string]*entry
	rmx sync.RWMutex
}
type entry struct {
	ch      chan struct{} //用于通讯的 channel
	value   interface{}   //值
	isExist bool          //值是否有效；  true，值是有效的； false，值是无效的
}

func (m *Map) Init() {
	m.c = make(map[string]*entry)
}

//@breif map的set操作
func (m *Map) Out(key string, val interface{}) {
	m.rmx.Lock()
	defer m.rmx.Unlock()
	if e, ok := m.c[key]; ok {
		fmt.Printf("111111111111\n")
		e.value = val
		e.isExist = true
		close(e.ch)
	} else {
		fmt.Printf("222222222222\n")
		e = &entry{ch: make(chan struct{}), isExist: true, value: val}
		m.c[key] = e
		close(e.ch)
	}
}

//@brief map的get操作
func (m *Map) Rd(key string, timeout time.Duration) interface{} {
	m.rmx.Lock()
	if e, ok := m.c[key]; ok && e.isExist { //map查到  entry，且 entry值有效
		m.rmx.Unlock()
		return e.value
	} else if !ok { //map查不到 entry； 初始化 entry（ isExist填写为false，即值无效，因为还没进行 map的set操作）； 然后等待 map的set操作；
		e = &entry{ch: make(chan struct{}), isExist: false}
		m.c[key] = e
		m.rmx.Unlock()
		fmt.Println("协程阻塞 -> ", key)
		select {
		case <-e.ch:
			return e.value
		case <-time.After(timeout):
			fmt.Println("协程超时 -> ", key)
			return nil
		}
	} else { //map查到了entry，  但是 isExist为fasle（值无效）；此时需要等待 map的set操作
		m.rmx.Unlock()
		fmt.Println("协程阻塞 -> ", key)
		select {
		case <-e.ch:
			return e.value
		case <-time.After(timeout):
			fmt.Println("协程超时 -> ", key)
			return nil
		}
	}
}

func main() {
	myWait := sync.WaitGroup{}

	var myMap Map
	myMap.Init()

	strKey := "key1"

	myWait.Add(1)

	//睡眠时间
	iSleepSec1 := 3 //协程睡眠时间
	iSleepSec2 := 5 //主线程睡眠时间

	//睡眠时间大小，决定查询结果
	//  iSleepSec1 >= iSleepSec2 ，则能查到 value
	//  iSleepSec1 <  iSleepSec2 ，查不到 value

	go func(wait *sync.WaitGroup) {
		value := myMap.Rd(strKey, time.Second*time.Duration(iSleepSec1))
		fmt.Printf("mapRd|value=%v\n", value)

		wait.Done()
	}(&myWait)

	time.Sleep(time.Second * time.Duration(iSleepSec2))
	myMap.Out(strKey, 1)

	myWait.Wait()

	var iTmp int
	fmt.Printf("iTmp=%v\n", iTmp)
}
